<template>
  <div class="executor-main">
    <el-container class="container-item" direction="vertical" style="padding: 0;">
      <!-- 展示用户的输入框 -->
      <el-container>
        <div v-if="inputDialogVisible" class="container-item focus-bottom">
          <div style="overflow: hidden;">
            <img :src="getImageUrl()" alt="元芳" style="float: right" class="yuanfang-avatar">
          </div>
          <div style="clear: both;" />
          <div>
            <el-form class="content-wrapper dialog-box" @submit.native.prevent="submitForm">
              <div v-for="(arg, argIndex) in step.userInput" :key="argIndex">
                <div v-if="arg.sourceType === 'input'">
                  <template v-if="arg.sourceData.type === 'singleSelect'">
                    <el-form-item :label="arg.sourceData.option.prompt">
                      <br>
                      <div v-for="(option, optionIndex) in arg.sourceData.option.options" :key="optionIndex">
                        <el-radio v-model="singleSelect" :label="option.value">{{ option.name }}</el-radio>
                      </div>
                    </el-form-item>
                  </template>
                  <template v-else-if="arg.sourceData.type === 'multiSelect'">
                    <el-form-item :label="arg.sourceData.option.prompt">
                      <br>
                      <div v-for="(option, optionIndex) in arg.sourceData.option.options" :key="optionIndex">
                        <el-checkbox v-model="selectedOptions" :label="option.value">{{ option.name }}</el-checkbox>
                      </div>
                    </el-form-item>
                  </template>
                  <template v-else-if="arg.sourceData.type === 'editBox'">
                    <el-form-item :label="arg.sourceData.option.prompt">
                      <el-input
                        v-if="arg.sourceData.option.inputMode === 'singleLine'"
                        v-model="arg.sourceData.option.defaultValue"
                        v-focus="argIndex === 0"
                        @keyup.enter="submitForm"
                      />
                      <el-input
                        v-if="arg.sourceData.option.inputMode === 'multiLine'"
                        v-model="arg.sourceData.option.defaultValue"
                        v-focus="argIndex === 0"
                        type="textarea"
                        :autosize="{ minRows: 1, maxRows: 1000 }"
                        @keyup.enter="submitForm"
                      />
                      <el-input
                        v-if="arg.sourceData.option.inputMode === 'password'"
                        v-model="arg.sourceData.option.defaultValue"
                        v-focus="argIndex === 0"
                        type="password"
                        @keyup.enter="submitForm"
                      />
                    </el-form-item>
                  </template>
                </div>
                <div v-else-if="arg.sourceType === 'file'">
                  <el-form-item>
                    <el-upload
                      :http-request="uploadFile"
                      :before-remove="beforeRemoveFile"
                      drag
                      :limit="1"
                      :on-success="handleSuccess"
                      :on-exceed="handleExceed"
                      :file-list="fileList"
                      style="margin-top: 15px"
                    >
                      <i class="el-icon-upload" />
                      <div class="el-upload__text">{{ $t('Drag_file_tips') }}<em>{{ $t('click') + $t('upload') }}</em>
                      </div>
                    </el-upload>
                  </el-form-item>
                </div>
              </div>
              <el-form-item style="text-align: center">
                <el-button type="primary" @click="submitForm">{{ $t('submit') }}</el-button>
                <el-button @click="cancelExecution">{{ $t('cancel') }}</el-button>
              </el-form-item>
            </el-form>
          </div>
        </div>
      </el-container>

      <!-- 思考中 -->
      <el-container v-if="waitingTaskList && !executionCompleted">
        <div class="container-item focus-bottom">
          <!-- 思考中的主体 -->
          <div class="content-wrapper dialog-box" style="margin-top: 10px;">
            <div
              v-loading="waitingTaskList"
              :element-loading-text="$t('appThinking')"
              element-loading-spinner="el-icon-loading"
              style="padding: 10px; height: 40px"
            />
          </div>
        </div>
      </el-container>

      <!-- 重新执行等操作按钮 -->
      <el-container>
        <div class="container-item">
          <div class="button-container">
            <!--  重新执行等操作按钮 -->
            <el-button v-if="executionCompleted || errorMessage" @click="restartExecution">{{ $t('re_run') }}</el-button>
            <!-- 退出应用按钮 -->
            <el-button @click="toDetail">{{ $t('back_to_detail') }}</el-button>
            <!-- 查看日志按钮 -->
            <el-button @click="toLog">{{ $t('view_log') }}</el-button>
            <!-- 编辑应用按钮 -->
            <el-button @click="toEdit">{{ $t('edit_app') }}</el-button>
          </div>
        </div>
      </el-container>
    </el-container>
  </div>
</template>
<script>
import { clearExecutorCount, executeAppStep, getAppDetail, getExecutroMemoryBus } from '@/api/executor'
import * as echarts from 'echarts'
import { uploadFile } from '@/api/executor'
import { getAppById } from '@/api/app'
import Autosize from 'vue-autosize'

export default {
  name: 'exe',
  directives: {
    autosize: Autosize
  },
  props: {
    appId: {
      type: Number,
      default: 0,
      require: true
    },
    history: {
      type: Array,
      default: () => [],
      require: true
    },
    hasExit: {
      type: Boolean,
      default: true
    },
    cancel: {
      type: Boolean,
      default: true
    }
  },
  data() {
    return {
      // 应用信息
      appName: null,
      originalAppDetail: null,

      memoryBus: null,
      inputDialogVisible: false,
      waitingTaskList: false,
      errorMessage: false,
      singleSelect: null, // 记录单选框的值
      selectedOptions: [], // 记录复选框的值

      // 执行信息
      nowOnTask: 0,
      nowTaskContent: null,
      nowTaskArgs: null,
      args: null,
      step: null,
      appDetail: null,

      /* 上传文件相关 */
      fileList: [],
      file: null,
      fileData: null,
      formData: null,
      img: null,
      downloadLink: null,
      fileName: null,

      avatarIndex: 0
    }
  },
  computed: {
    notRunToEnd() {
      return this.nowOnTask < this.appDetail.script.length
    },
    executionCompleted() {
      return this.appDetail.script && this.nowOnTask >= this.appDetail.script.length
    }
  },
  watch: {
    nowOnTask: {
      handler(newVal) {
        if (this.appDetail.script && newVal >= this.appDetail.script.length) {
          this.history.push({
            'speaker': 'app',
            'type': 'text',
            'data': `"${this.appName}"${this.$t('finished_running')}`,
            'speakerId': this.appId
          })
          this.cancelExecution()
        }
      }
    },
    // 当出现思考中时，滚动到思考中处
    waitingTaskList: {
      handler(newVal) {
        if (newVal) {
          this.$nextTick(() => {
            this.scrollToInput()
          })
        }
      }
    },
    errorMessage: {
      handler(newVal) {
        if (newVal) {
          this.$nextTick(() => {
            this.scrollToCompletion()
          })
        }
      }
    }
  },
  created() {
    if (this.$store.getters.avatar !== '') {
      this.avatarIndex = parseInt(this.$store.getters.avatar) + 1
    } else {
      this.avatarIndex = 1
    }
    this.$store.commit('util/CHANGE_PREVIOUSAPPID', this.appId)
    this.getDetailAndShow()
  },
  methods: {
    getDetailAndShow() {
      getAppById(this.appId).then(async res => {
        console.log('获取到的应用详情:', res)
        this.appDetail = res
        this.appDetail.script = JSON.parse(this.appDetail.script)
        this.originalAppDetail = JSON.parse(JSON.stringify(this.appDetail))
        this.appName = res.app_name
        this.history.push({
          'speaker': 'user',
          'type': 'input',
          'data': [`${this.$t('launch')}${this.$t('application')}: ${this.appName}`],
          // 'data': [`<strong>${this.$t('launch')}${this.$t('application')}:</strong> ${this.appName}`],
          'speakerId': this.$store.getters.id
        })
        // 每次清空执行器的计数
        await clearExecutorCount(this.$store.getters.id ? this.$store.getters.id : -1).then(res => {
          console.log(res)
        })
        // 获取图片ID
        this.imageId = this.getImageId(this.appName)
        this.autoFillArgs(this.nowOnTask)
      }).catch(error => {
        console.log('执行时获取详情出错:', error)
      })
    },
    async autoFillArgs(task) {
      this.nowOnTask = task
      this.nowTaskContent = this.appDetail.script.at(task)
      this.nowTaskArgs = []
      this.args = this.nowTaskContent.parameters
      await getExecutroMemoryBus().then((res) => {
        this.memoryBus = res
        console.log(res)
      }).catch(error => {
        console.log('子组件填参', error)
      })

      // 初始化input的value，展示正确的默认值
      this.args.forEach(arg => {
        if (arg.sourceType === 'input' && arg.sourceData.type === 'editBox') {
          // 如果该值为profile.开头，则默认其为使用profile中的变量；如果没取到对应变量，则不进行改变
          try {
            if (arg.sourceData.option.defaultValue.startsWith('profile')) {
              const keys = arg.sourceData.option.defaultValue.split('.')
              let current = this.memoryBus
              for (const key of keys) {
                current = current[key]
              }
              arg.sourceData.option.defaultValue = current
            }
          } catch (e) {
            console.log(e)
          }
        } else if (arg.sourceType === 'input' && arg.sourceData.type === 'singleSelect') {
          for (let i = 0; i < arg.sourceData.option.options.length; i++) {
            if (arg.sourceData.option.options[i].checked === 'true') {
              arg.sourceData.option.variableName = arg.name
              this.singleSelect = arg.sourceData.option.options[i].value
              break
            }
          }
        } else if (arg.sourceType === 'input' && arg.sourceData.type === 'multiSelect') {
          arg.sourceData.option.defaultValue = ''
        }
      })

      const hasInputArgs = this.args.some(arg => arg.sourceType === 'input') || this.args.some(arg => arg.sourceType === 'file')
      this.step = {
        userInput: this.args,
        task_id: this.nowOnTask + 1,
        canceled: false
      }
      if (hasInputArgs) {
        this.inputDialogVisible = true
      } else {
        await this.submitForm()
      }
      this.$nextTick(() => {
        this.scrollToInput()
      })
    },
    async submitForm() {
      try {
        if (this.fileList) {
          this.appDetail.file = this.fileData
        }

        this.memoryBus = await getExecutroMemoryBus()
        this.args.forEach(arg => {
          if (arg.sourceType === 'refer') {
            const path = arg.sourceData.option.defaultValue.split('.')
            let value = this.memoryBus
            // 如果路径的第一个部分是 'codeVar'，则跳过
            if (path[0] === 'codeVar') {
              path.shift() // 移除 'codeVar'
            }
            for (let i = 0; i < path.length; i++) {
              value = value[path[i]]
              if (value === undefined) break
            }
            arg.sourceData.option.defaultValue = value
            arg.sourceData.option.variableName = arg.name
          } else if (arg.sourceType === 'input' && arg.sourceData.type === 'singleSelect') {
            arg.sourceData.option.defaultValue = this.singleSelect
          }
        })

        // 修改参数为真正提交的
        // this.args.reduce((acc, arg) => {
        //   if (arg.sourceType === 'input') {
        //     if (arg.input.inputType === 'editBox') {
        //       acc[arg.name] = arg.value
        //     } else if (arg.input.inputType === 'singleSelect') {
        //       acc[arg.name] = arg.value
        //     } else if (arg.input.inputType === 'multiSelect') {
        //       arg.value = this.selectedOptions.join(';')
        //       acc[arg.name] = arg.value
        //     }
        //   } else if (arg.sourceType === 'const') {
        //     acc[arg.name] = {
        //       value: arg.value,
        //       type: 'Const'
        //     }
        //   } else if (arg.sourceType === 'refer') {
        //     acc[arg.name] = {
        //       value: arg.value,
        //       type: 'Refer'
        //     }
        //   }
        //   if (arg.sourceType === 'file') {
        //     arg.value = this.fileName
        //     // console.log('3333', arg.value)
        //   }
        //   return acc
        // }, {})

        const inputArgs = []
        for (let i = 0; i < this.args.length; i++) {
          if (this.args[i].sourceType === 'file') {
            this.history.push({
              'speaker': 'user',
              'type': 'file',
              'filename': this.args[i].value,
              'fileData': this.fileData,
              'file': this.file,
              'description': this.args[i].description,
              'speakerId': this.$store.getters.id
            })
          } else if (this.args[i].sourceType === 'input') {
            inputArgs.push(this.args[i].sourceData.option.prompt + ':  ' + this.args[i].sourceData.option.defaultValue)
          }
        }
        if (inputArgs.length !== 0) {
          this.history.push({
            'speaker': 'user',
            'type': 'input',
            'data': inputArgs,
            'speakerId': this.$store.getters.id
          })
        }

        if (this.inputDialogVisible) {
          this.inputDialogVisible = false
        }

        if (!this.waitingTaskList) {
          this.waitingTaskList = true
        }
        console.log('执行前的', this.appDetail)
        // Process next step
        await executeAppStep({ 'appDetail': this.appDetail, 'id': this.$store.getters.id }).then(async response => {
          console.log('单步执行task：', response)
          await this.showChart()
          if (this.notRunToEnd) {
            this.autoFillArgs(this.nowOnTask)
          }
        }).catch(error => {
          let errorMessage = ''
          let flag = false // 判断是否是跳转或者中止执行
          if (this.waitingTaskList) {
            this.waitingTaskList = false
          }
          if (!error.response.data.message) {
            errorMessage = '未知错误'
          } else {
            errorMessage = error.response.data.message
            // 胶水代码用户中止执行，就会执行if, 跳转就会执行else if
            if (error.response.data.message === 'error to execute: Aborting the current program') {
              this.$message.warning('The user has aborted the current program')
              errorMessage = '用户主动中止当前应用'
              flag = true
            } else if (error.response.data.message.startsWith('error to execute: Error in user function: Skip to step ')) {
              this.appDetail = JSON.parse(JSON.stringify(this.originalAppDetail))
              // 获取用户要跳转的步骤
              const step = parseInt(error.response.data.message.slice(55))
              this.nowOnTask = step - 1
              this.autoFillArgs(this.nowOnTask)
              return true
            } else if (error.response.data.message.startsWith('error to execute: Error in user function: Skip back to step ')) {
              this.appDetail = JSON.parse(JSON.stringify(this.originalAppDetail))
              // 获取用户要向后跳转的步骤
              const step = parseInt(error.response.data.message.slice(60))
              this.nowOnTask = step - 1
              this.autoFillArgs(this.nowOnTask)
              return true
            }
          }
          if (flag) {
            this.history.push({
              'speaker': 'app',
              'type': 'stop-app',
              'data': errorMessage,
              'speakerId': this.appId
            })
          } else {
            this.history.push({
              'speaker': 'app',
              'type': 'error-message',
              'showError': false,
              'data': errorMessage,
              'speakerId': this.appId
            })
          }
          console.log('执行单步task失败：', error.response.data)
          this.errorMessage = true
        })
      } catch (error) {
        console.log('获取MemoryBus失败:', error)
      }

      this.$nextTick(() => {
        this.scrollToInput()
      })
    },
    async showChart() {
      await getExecutroMemoryBus().then(res => {
        const results = res.yf_processed_output.result
        this.waitingTaskList = false
        if (results && results.length > 0) {
          results.forEach((item, index) => {
            if (item.type === 'echart' && item.data) {
              this.history.push({
                'speaker': 'app',
                'type': 'echart',
                'data': item.data,
                'speakerId': this.appId
                // 'avatar': this.getImageUrl()
              })
            } else if (item.type === 'text' && item.data) {
              this.history.push({
                'speaker': 'app',
                'type': 'text',
                'data': item.data,
                'speakerId': this.appId
                // 'avatar': this.getImageUrl()
              })
            } else if (item.type === 'file') {
              this.history.push({
                'speaker': 'app',
                'type': 'file',
                'data': item.data,
                'speakerId': this.appId
                // 'avatar': this.getImageUrl()
              })
            }
            // 如果不是第一个元素，添加notDisplayAvatar: false
            if (index > 0) {
              this.history[this.history.length - 1].notDisplayAvatar = 'none'
            }
          })
        } else {
          console.warn('No chart option available.')
        }
      }).catch(error => {
        console.log('processOutput出错:', error)
      })
      this.nowOnTask += 1
    },
    scrollToCompletion() {
      this.$nextTick(() => {
        window.scrollTo({
          top: document.body.scrollHeight, // 页面底部的绝对位置
          behavior: 'smooth' // 平滑滚动
        })
      })
    },
    scrollToInput() {
      this.$nextTick(() => {
        // Ensure that DOM updates have completed before trying to scroll
        const formItem = this.$el.querySelector('.focus-bottom:last-of-type')
        if (formItem) {
          const top = formItem.getBoundingClientRect().top + window.scrollY
          window.scrollTo({
            top,
            behavior: 'smooth' // Smooth scrolling
          })
        } else {
          console.error('Form item not found')
        }
      })
    },
    cancelExecution() {
      // 默认点击取消，中止应用执行
      if (this.cancel) {
        let errorMessage = ''
        errorMessage = '用户主动退出了应用'
        this.history.push({
          'speaker': 'app',
          'type': 'stop-app',
          'data': errorMessage,
          'speakerId': this.appId
        })
        this.errorMessage = true
        this.inputDialogVisible = false
      } else {
        this.$emit('cancelExecution')
      }
    },
    getImageId(appName) {
      if (!appName || appName.length === 0) return 0
      const firstChar = appName[0].toUpperCase() // 取应用名称的首字母并转为大写
      const charCode = firstChar.charCodeAt(0) // 转换为字符编码
      return charCode % 20 // 对20取模，得到0-19
    },
    // 获取图片URL
    getImageUrl() {
      return require(`@/assets/icons/${this.imageId}.png`)
    },

    /* 文件相关 */
    // 上传文件的请求发送函数
    uploadFile(param) {
      this.file = param.file
      this.formData = new FormData()
      // 获取文件名称
      this.fileName = param.file.name
      this.formData.append('file', param.file)
      uploadFile(this.formData).then(response => {
        console.log('上传文件成功', response)
        // 保存二进制格式的文件
        this.fileData = response
        this.$emit('save-file', response)
        // 文件图标设为绿色
        param.onSuccess()
      }).catch(error => {
        console.log('上传文件失败', error)
        // param.onError()
      })
    },
    // 移除上传文件的处理函数
    beforeRemoveFile(file, fileList) {
      return this.$confirm(`${this.$t('sure_remove_tip')}${file.name}?`)
    },
    // 上传文件个数超过限制的处理函数
    handleExceed(files, fileList) {
      this.$message.warning(`${this.$t('file_upload_tip_one')}，${this.$t('file_upload_tip_two')} ${files.length} ${this.$t('file_upload_tip_four')}，${this.$t('file_upload_tip_three')} ${files.length + fileList.length} ${this.$t('file_upload_tip_four')}`)
    },
    handleSuccess(response, file, fileList) {
      this.fileList = fileList
    },
    createImageFromBinaryStream(base64String) {
      // 将Base64字符串解码为二进制数据
      const binaryStream = atob(base64String) // atob() 用于解码Base64字符串
      const len = binaryStream.length
      const bytes = new Uint8Array(len)
      for (let i = 0; i < len; i++) {
        bytes[i] = binaryStream.charCodeAt(i)
      }
      // 将二进制流转换为Blob对象
      const blob = new Blob([bytes], { type: 'image/png' }) // 根据实际图片类型设置MIME类型

      // 创建一个临时URL
      const imageUrl = URL.createObjectURL(blob)

      // 创建img元素
      self.img = document.createElement('img')
      self.img.src = imageUrl

      // 设置最大宽度和最大高度
      self.img.style.maxWidth = '100%'
      self.img.style.maxHeight = '500px'

      // 设置object-fit属性
      self.img.style.objectFit = 'contain'

      // 设置height属性
      self.img.style.height = 'auto'

      // 将img元素添加到特定的div中
      // const imageContainer = document.getElementById('image_load')
      // imageContainer.appendChild(self.img)

      // 创建下载链接
      self.downloadLink = document.createElement('a')
      self.downloadLink.href = imageUrl
      self.downloadLink.download = 'downloaded_image.png' // 指定下载的文件名
      self.downloadLink.textContent = '下载'

      // 将下载链接添加到DOM中
      // imageContainer.appendChild(self.downloadLink)

      // 在不需要时释放URL对象
      // URL.revokeObjectURL(imageUrl);
    },
    // 重新执行
    restartExecution() {
      // 清空执行信息
      this.nowOnTask = 0
      this.nowTaskArgs = null
      this.nowTaskContent = null
      this.args = null
      this.step = null
      // 清空文件信息
      this.fileList = null
      this.file = null
      this.fileData = null
      this.formData = null
      this.img = null
      this.downloadLink = null
      this.fileName = null
      // 清空状态信息
      this.memoryBus = null
      this.inputDialogVisible = false
      this.waitingTaskList = false
      this.errorMessage = false
      this.getDetailAndShow() // 重新获取应用详情并初始化
    }
  }
}
</script>

<style scoped>
.user-avatar{
  cursor: pointer;
  width: 40px;
  height: 40px;
  border-radius: 10px;
  margin-bottom: 5px;
}
.content-wrapper{
  width: 100%;
  background-color: white;
  padding: 15px;
}

.history{
  width: 100%;
}
.container-item{
  width: 100%;
}
.dialog-box {
  border-radius: 10px;
}
.button-container {
  display: flex;
  justify-content: center;
  margin-top: 10px;
}
.yuanfang-avatar {
  cursor: pointer;
  width: 40px;
  height: 40px;
  border-radius: 10px;
  margin-bottom: 10px;
}
</style>
